﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
using PaymentHelper.Core;
using PaymentHelper.Core.Extension;
using PaymentHelper.Core.RelatedObject;
using PaymentHelper.Core.ExceptionHandler;
using PaymentHelper.Core.ViewEmbeddedHandler;
using System.Web.Hosting;
using PaymentHelper.Core.CommonUtils;

namespace PaymentHelper
{
    public class PaymentManager
    {
        #region Properties

        private static PaymentManager _instance;
        private SessionStorage _storage;
        private static PaymentStateCollection _paymentStateCollection;

        /// <summary>
        /// Gets and sets instance
        /// </summary>
        public static PaymentManager Instance 
        { 
            get
            {
                if (_instance == null)
                    _instance = new PaymentManager();

                return _instance;
            } 
            private set 
            {
                _instance = value;
            }
        }

        #endregion

        /// <summary>
        /// Private constructor of PaymentManager
        /// </summary>
        private PaymentManager()
        {
            _storage = new SessionStorage("PH_PaymentResult.PaymentState.Token");
            _paymentStateCollection = new PaymentStateCollection();
        }

        /// <summary>
        /// Load all methods
        /// </summary>
        /// <returns></returns>
        public IList<IPaymentMethod> LoadAllPaymentMethod(bool installedOnly = true, bool enableOnly = true)
        {
            try
            {
                return ExtensionManager.LoadAllExtensions<IPaymentMethod>(installedOnly,enableOnly);
            }
            catch (PaymentHelperException e)
            {
                throw e;
            }
        }

        /// <summary>
        /// Load all payment method by currency accepted
        /// </summary>
        /// <param name="currencyCode">specifid currency code</param>
        /// <param name="installedOnly"></param>
        /// <param name="enableOnly"></param>
        /// <returns></returns>
        public IList<IPaymentMethod> LoadAllPaymentMethod(string currencyCode, bool installedOnly = true, bool enableOnly = true)
        {
            var listMethod = LoadAllPaymentMethod(installedOnly, enableOnly);
            var result = new List<IPaymentMethod>();

            foreach (var method in listMethod)
            {
                if (method.AcceptCurrency(currencyCode))
                    result.Add(method);
            }

            return result;            
        }

        /// <summary>
        /// Load method specified by SystemName
        /// </summary>
        /// <param name="systemName">the systemname of method</param>
        /// <returns>
        /// null: if not exist
        /// IPaymentMethod: if exist
        /// </returns>
        /// 
        public IPaymentMethod LoadPaymentMethodBySystemName(string systemName, bool installedOnly = true, bool enableOnly = true)
        {
            try
            {
                return ExtensionManager.LoadExtensionBySystemName<IPaymentMethod>(systemName, installedOnly, enableOnly);
            }
            catch (PaymentHelperException e)
            {
                throw e;
            }
        }

        /// <summary>
        /// Load BaseExtension by systemName
        /// </summary>
        /// <param name="systemName"></param>
        /// <returns></returns>
        public BaseExtension LoadExtensionBySystemName(string systemName, bool installedOnly = true, bool enableOnly = true)
        {
            try
            {
                return ExtensionManager.LoadExtensionBySystemName<BaseExtension>(systemName, installedOnly, enableOnly);
            }
            catch (PaymentHelperException e)
            {
                throw e;
            }
        }

        /// <summary>
        /// Load System Name of Default PaymentMethod
        /// </summary>
        /// <returns></returns>
        public string GetSystemNameOfDefaultPaymentMethod()
        {
            try
            {
                return ExtensionManager.LoadSystemNameDefaultPaymentMethod();
            }
            catch (PaymentHelperException ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Generate a token for current payment
        /// </summary>
        /// <param name="salt">any string</param>
        /// <returns></returns>
        public string GeneratePaymentKey(string salt)
        {
            string input = DateTime.UtcNow.Ticks.ToString() + (string.IsNullOrEmpty(salt) ? "" : salt);
            string token = PHUtility.MD5(input);

            _storage.SaveObject(token);
            _paymentStateCollection.AddState(token);

            return token;
        }

        /// <summary>
        /// Update result of current payment
        /// </summary>
        /// <param name="result"></param>
        public void UpdateCurrentPaymentState(PaymentResult result)
        {
            var state = GetCurrentPaymentState();

            if (state != null)
                state.Result = result;
        }

        /// <summary>
        /// Update state of payment with specified payment token
        /// </summary>
        /// <param name="token">token Payment</param>
        /// <param name="urlProcessPayment"> url process payment</param>
        public void UpdatePaymentState(string token, string urlProcessPayment)
        {
            _paymentStateCollection.UpdateState(token, urlProcessPayment);
        }

        /// <summary>
        /// Update state of payment with specified payment token
        /// </summary>
        /// <param name="token"></param>
        /// <param name="result"></param>
        public void UpdatePaymentState(string token, PaymentResult result)
        {
            _paymentStateCollection.UpdateState(token, result);
        }

        /// <summary>
        /// Get payment state by token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public PaymentState GetPaymentState(string token)
        {
            return _paymentStateCollection.GetState(token);
        }

        /// <summary>
        /// Get current payment state
        /// </summary>
        /// <returns></returns>
        public PaymentState GetCurrentPaymentState()
        {
            string token = (string)_storage.GetObject();
            
            return string.IsNullOrEmpty(token) ? null : _paymentStateCollection.GetState(token);
        }

        /// <summary>
        /// Load default payment method
        /// </summary>
        /// <returns></returns>
        public IPaymentMethod GetDefaultPaymentMethod()
        {
            string systemName = GetSystemNameOfDefaultPaymentMethod();
            
            return LoadPaymentMethodBySystemName(systemName);
        }

        /// <summary>
        /// Install a payment method
        /// </summary>
        /// <param name="systemName"></param>
        /// <returns></returns>
        public bool InstallPaymentMethod(string systemName)
        {
            var method = LoadExtensionBySystemName(systemName);
            if (method == null)
                throw GetExceptionNotFoundMethod(systemName, System.Reflection.MethodBase.GetCurrentMethod().Name);
            return method.Install();
        }

        /// <summary>
        /// UnInstall a payment method
        /// </summary>
        /// <param name="systemName"></param>
        /// <returns></returns>
        public bool UnInstallPaymentMethod(string systemName)
        {
            var method = LoadExtensionBySystemName(systemName);
            if (method == null)
                throw GetExceptionNotFoundMethod(systemName, System.Reflection.MethodBase.GetCurrentMethod().Name);
            return method.UnInstall();
        }

        /// <summary>
        /// Load settings of payment method specified
        /// </summary>
        /// <param name="systemName"></param>
        /// <returns></returns>
        public IList<PHSettingItem> LoadSettings(string systemName)
        {
            var method = LoadExtensionBySystemName(systemName);
            if (method == null)
            {
                throw GetExceptionNotFoundMethod(systemName, System.Reflection.MethodBase.GetCurrentMethod().Name);
            }

            return method.LoadSettings();
        }

        /// <summary>
        /// Save settings of payment method specified
        /// </summary>
        /// <param name="systemName"></param>
        /// <param name="listAtrr"></param>
        /// <returns></returns>
        public bool SaveSettings(string systemName,IList<PHSettingItem> listAtrr)
        {
            var method = LoadExtensionBySystemName(systemName);
            if (method == null)
            {
                throw GetExceptionNotFoundMethod(systemName,System.Reflection.MethodBase.GetCurrentMethod().Name);
            }

            method.Settings = listAtrr;
            return method.SaveSettings();
        }

        /// <summary>
        /// Load all embedded views (view files MVC)
        /// </summary>
        public static void LoadEmbeddedViews()
        {
            ViewEmbedded viewEmbedded = new ViewEmbedded(AssemblyLoader.Instance);
            var embeddedViewVirtualPathProvider = new EmbeddedViewVirtualPathProvider(viewEmbedded.GetEmbeddedViews());
            HostingEnvironment.RegisterVirtualPathProvider(embeddedViewVirtualPathProvider);
        }

        /// <summary>
        /// Register all routes 
        /// </summary>
        /// <param name="routes"></param>
        public static void RegisterRoutes(RouteCollection routes)
        {
            //Load all method 
            var listMethod = ExtensionManager.LoadAllExtensions<IPaymentMethod>(false,false);
            
            if (listMethod != null && listMethod.Count > 0)
            {
                foreach (var method in listMethod)
                {
                    method.RegisterRoutes(routes);
                }
            }
        }

        /// <summary>
        /// Get exception for error "Method not found"
        /// </summary>
        /// <param name="function">function which throws exception</param>
        /// <param name="level">level exception logged by Log4net</param>
        /// <returns>instance of PaymentHelperException</returns>
        protected PaymentHelperException GetExceptionNotFoundMethod(string systemName, string function, ExceptionLevel level = ExceptionLevel.Warning)
        {
            string message = string.Format("Load the payment method '{0}' failed !", systemName);
            string sender = string.Format("{0}.{1}", this.GetType().ToString(), function);

            return new PaymentHelperException(level, sender, message);
        }
    }
}
